﻿using SocketCore.Common.Config;
using SocketCore.Common.Event;
using System.Net.Sockets;

namespace SocketCore.Common.Heartbeat;

/// <summary>
/// 构造
/// </summary>
/// <param name="heartbeatConfig"></param>
/// <param name="isServer"></param>
public sealed class HeartbeatManager(HeartbeatConfig heartbeatConfig, bool isServer)
{
    /// <summary>
    /// 配置
    /// </summary>
    private readonly HeartbeatConfig _heartbeatConfig = heartbeatConfig;

    /// <summary>
    /// 服务类型
    /// </summary>
    public readonly string ServerType = isServer ? "客户端" : "服务端";

    /// <summary>
    /// 心跳定时器
    /// </summary>
    private PeriodicTimer? _heartbeatTimer;

    /// <summary>
    /// 心跳定时器取消令牌
    /// </summary>
    private CancellationTokenSource? _heartbeatCts;

    /// <summary>
    /// 客户端状态
    /// </summary>
    public readonly System.Collections.Concurrent.ConcurrentDictionary<Socket, ClientHeartbeatState> ClientStates = new();

    /// <summary>
    /// 启用 TCP KeepAlive
    /// </summary>
    /// <param name="socket"></param>
    /// <param name="enable"></param>
    /// <param name="keepAliveTime"></param>
    /// <param name="keepAliveInterval"></param>
    public void SetSocketKeepAlive(Socket socket, bool enable, int keepAliveTime, int keepAliveInterval)
    {
        try
        {
            byte[] inValue = new byte[12];
            BitConverter.GetBytes(enable ? 1u : 0u).CopyTo(inValue, 0);
            BitConverter.GetBytes(keepAliveTime).CopyTo(inValue, 4);
            BitConverter.GetBytes(keepAliveInterval).CopyTo(inValue, 8);

            socket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
        }
        catch (Exception ex)
        {
            _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.Log, new SocketLogMessage { Level = LogLevel.Error, Message = $"TCP KeepAlive 发生错误：{ex.Message}", Exception = ex });
        }
    }

    /// <summary>
    /// 启动心跳检测
    /// </summary>
    public void StartHeartbeatCheck()
    {
        _heartbeatCts = new CancellationTokenSource();
        _heartbeatTimer = new PeriodicTimer(TimeSpan.FromMilliseconds(_heartbeatConfig.HeartbeatTimeoutMs / 2));
        _ = CheckHeartbeatAsync(_heartbeatCts.Token);
    }

    /// <summary>
    /// 心跳检测
    /// </summary>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    private async Task CheckHeartbeatAsync(CancellationToken cancellationToken)
    {
        try
        {
            while (await _heartbeatTimer!.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false))
            {
                var now = DateTime.UtcNow;
                foreach (var clientPair in ClientStates)
                {
                    var clientSocket = clientPair.Key;
                    var state = clientPair.Value;

                    if ((now - state.LastHeartbeatTime).TotalMilliseconds > _heartbeatConfig.HeartbeatTimeoutMs)
                    {
                        _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.Heartbeat, new SocketHeartbeatMessage { Message = $"{ServerType}[{clientSocket.RemoteEndPoint}]心跳检测超时" });
                        _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.HeartbeatTimeout, clientSocket);
                        // 断开客户端
                        state.ReceiveCts?.Cancel();
                        _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.Log, new SocketLogMessage { Level = LogLevel.Debug, Message = $"HeartbeatManager.CheckHeartbeatAsync()" });
                        RemoveClient(clientSocket);
                    }
                }
            }
        }
        catch (OperationCanceledException)
        {
            // 正常取消
        }
    }

    /// <summary>
    /// 发送心跳消息
    /// </summary>
    /// <param name="clientSocket"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task SendHeartbeatsAsync(Socket clientSocket, CancellationToken cancellationToken)
    {
        try
        {
            while (!cancellationToken.IsCancellationRequested && clientSocket.Connected)
            {
                await Task.Delay(_heartbeatConfig.HeartbeatIntervalMs, cancellationToken);

                if (clientSocket.Connected && !cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        _heartbeatConfig.EventHub.SendHeartbeatCallback?.Invoke(clientSocket, cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.Log, new SocketLogMessage { Level = LogLevel.Error, Message = $"发送{ServerType}[{clientSocket.RemoteEndPoint}]心跳消息时发生错误：{ex.Message}", Exception = ex });
                        break; // 发生错误时退出循环
                    }
                }
                else
                {
                    break; // 连接断开或取消时退出循环
                }
            }
        }
        catch (OperationCanceledException)
        {
            // 任务被取消，正常退出
        }
        catch (Exception ex)
        {
            _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.Log, new SocketLogMessage { Level = LogLevel.Error, Message = $"发送{ServerType}[{clientSocket.RemoteEndPoint}]心跳消息时发生错误：{ex.Message}", Exception = ex });
        }
    }

    /// <summary>
    /// 删除客户端
    /// </summary>
    /// <param name="clientSocket"></param>
    public void RemoveClient(Socket clientSocket)
    {
        if (ClientStates.TryRemove(clientSocket, out var state))
        {
            state.ReceiveCts?.Cancel();
            state.ReceiveCts?.Dispose();
        }

        if (ServerType == "客户端")
        {
            _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.Log, new SocketLogMessage { Level = LogLevel.Info, Message = $"[{clientSocket.RemoteEndPoint}]{ServerType}已断开连接" });
            _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.Connect, new SocketClientConnect { ClientInfo = clientSocket.RemoteEndPoint?.ToString(), IsConnect = false });
        }

        try
        {
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();
        }
        catch
        {
            // 忽略关闭时的异常
        }
    }

    /// <summary>
    /// Dispose
    /// </summary>
    public void Dispose()
    {
        _heartbeatConfig.EventHub.RaiseEvent(SocketEventType.Log, new SocketLogMessage { Level = LogLevel.Debug, Message = $"HeartbeatManager.Dispose()" });
        // 清理所有客户端
        foreach (var client in ClientStates.Keys)
        {
            RemoveClient(client);
        }

        _heartbeatCts?.Cancel();
        _heartbeatTimer?.Dispose();
        _heartbeatCts?.Dispose();
    }
}